package warenautomat;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;

/**
 * Der Automat besteht aus 7 Drehtellern welche wiederum je aus 16 Fächer
 * bestehen. <br>
 * Der erste Drehteller und das jeweils erste Fach haben jeweils die Nummer 1
 * (nicht 0!). <br>
 * Im Weitern hat der Automat eine Kasse. Diese wird vom Automaten instanziert.
 */
public class Automat {

   private static final int NR_DREHTELLER = 7;
   private Drehteller mDrehteller[];
   private Kasse mKasse;
   private ArrayList<Ware> mWaren;
   private ArrayList<Bestellung> mBestellKonfig;
   

   private SimpleDateFormat formatter = new SimpleDateFormat("dd.MM.yyyy");

   /**
    * Der Standard-Konstruktor. <br>
    * Führt die nütigen Initialisierungen durch (u.a. wird darin die Kasse
    * instanziert).
    */
   public Automat() {
      mDrehteller = new Drehteller[NR_DREHTELLER];
      for (int i = 0; i < NR_DREHTELLER; i++) {
         mDrehteller[i] = new Drehteller(this, i+1);
      }
      
      mKasse = new Kasse();
      mWaren = new ArrayList<>();
      mBestellKonfig = new ArrayList<>();
      

      System.out.println("Automat::Automat(): ");
   }

   /**
    * Füllt ein Fach mit Ware. <br>
    * Wenn das Service-Personal den Automaten füllt, wird mit einem
    * Bar-Code-Leser zuerst die Ware gescannt. <br>
    * Daraufhin wird die Schiebe-Tür geöffnet. <br>
    * Das Service-Personal legt die neue Ware ins Fach und schliesst das Fach. <br>
    * Die Hardware resp. System-Software ruft die Methode
    * <code> Automat.fuelleFach() </code> auf.
    * 
    * @param pDrehtellerNr
    *           Der Drehteller bei welchem das Fach hinter der Schiebe-Türe
    *           gefüllt wird. <br>
    *           Nummerierung beginnt mit 1 (nicht 0)!
    * @param pWarenName
    *           Der Name der neuen Ware.
    * @param pPreis
    *           Der Preis der neuen Ware.
    * @param pVerfallsDatum
    *           Das Verfallsdatum der neuen Ware.
    */
   public void fuelleFach(int pDrehtellerNr, String pWarenName, double pPreis, Date pVerfallsDatum) {
      Ware fachInhalt = new Ware(pWarenName, mKasse.convFrankenToRappen(pPreis), pVerfallsDatum);
      
      mDrehteller[pDrehtellerNr - 1].fuelleFach(fachInhalt);
      mWaren.add(fachInhalt);

      System.out.println("Automat::fuelleFach("+pDrehtellerNr+", "+pWarenName+", "+pPreis+"Fr., "+formatter.format(pVerfallsDatum)+"): ");
   }

   /**
    * Gibt die Objekt-Referenz auf die <em> Kasse </em> zurück.
    */
   public Kasse gibKasse() {
      System.out.println("Automat::gibKasse(): mKasse");
      return mKasse;
   }

   /**
    * Wird von der System-Software jedesmal aufgerufen wenn der gelbe Dreh-Knopf
    * gedrückt wird. <br>
    * Die Applikations-Software führt die Drehteller-Anzeigen nach (Warenpreis,
    * Verfallsdatum). <br>
    * Das Ansteuern des Drehteller-Motors übernimmt hat die System-Software
    * (muss nicht von der Applikations-Software gesteuert werden.). <br>
    * Die System-Software stellt sicher, dass <em> drehen </em> nicht
    * durchgeführt wird wenn ein Fach offen ist.
    */
   public void drehen() {
      for (int i = 0; i < NR_DREHTELLER; i++) {
         mDrehteller[i].drehen();
      }

      SystemSoftware.dreheWarenInGui();
   }

   /**
    * Beim Versuch eine Schiebetüre zu üffnen ruft die System-Software die
    * Methode <code> oeffnen() </code> der Klasse <em> Automat </em> mit der
    * Drehteller-Nummer als Parameter auf. <br>
    * Es wird überprüft ob alles o.k. ist: <br>
    * - Fach nicht leer <br>
    * - Verfallsdatum noch nicht erreicht <br>
    * - genug Geld eingeworfen <br>
    * - genug Wechselgeld vorhanden <br>
    * Wenn nicht genug Geld eingeworfen wurde, wird dies mit
    * <code> SystemSoftware.zeigeZuWenigGeldAn() </code> signalisiert. <br>
    * Wenn nicht genug Wechselgeld vorhanden ist wird dies mit
    * <code> SystemSoftware.zeigeZuWenigWechselGeldAn() </code> signalisiert. <br>
    * Wenn o.k. wird entriegelt (<code> SystemSoftware.entriegeln() </code>) und
    * positives Resultat zurückgegeben, sonst negatives Resultat. <br>
    * Es wird von der System-Software sichergestellt, dass zu einem bestimmten
    * Zeitpunkt nur eine Schiebetüre offen sein kann.
    * 
    * @param pDrehtellerNr
    *           Der Drehteller bei welchem versucht wird die Schiebe-Türe zu
    *           öffnen. <br>
    *           Nummerierung beginnt mit 1 (nicht 0)!
    * @return Wenn alles o.k. <code> true </code>, sonst <code> false </code>.
    */
   public boolean oeffnen(int pDrehtellerNr) {
      Ware verkaufteWare = mDrehteller[pDrehtellerNr - 1].oeffnen();
      if (verkaufteWare != null) {
         mKasse.abbuchenBetrag(verkaufteWare.getPreisInRappen());
         bestelleWare(verkaufteWare.getName());
         SystemSoftware.entriegeln(pDrehtellerNr);
      }
      
      System.out.println("Automat::oeffnen("+pDrehtellerNr+"): " + (verkaufteWare != null?"erfolgreich":"fehlerhaft"));
      return (verkaufteWare != null);
   }

   /**
    * Gibt den aktuellen Wert aller im Automaten enthaltenen Waren in Franken
    * zurück (excl. abgelaufene Waren). <br>
    * Analyse: <br>
    * Abgeleitetes Attribut. <br>
    * 
    * @return Der totale Warenwert des Automaten in Franken.
    */
   public double gibTotalenWarenWert() {
      int wertInRappen = 0;
      
      for(int i = 0; i < mWaren.size(); i++){
         Ware ware = mWaren.get(i);
         if(ware != null){
            wertInRappen += ware.getPreisInRappen();
         }
      }

      System.out.println("Automat::gibTotalenWarenWert(): " + wertInRappen/100.0 +"Fr.");
      return wertInRappen/100.0;

   }

   /**
    * Gibt die Anzahl der verkauften Ware <em> pName </em> seit (>=)
    * <em> pDatum </em> Zahl zurück.
    * 
    * @param pName
    *           Der Name der Ware nach welcher gesucht werden soll.
    * @param pDatum
    *           Das Datum seit welchem gesucht werden soll.
    * @return Anzahl verkaufter Waren.
    */
   public int gibVerkaufsStatistik(String pName, Date pDatum) {
      int zaehleVerkaufteWare = 0;
      
      for(int i = 0; i < mWaren.size(); i++){
         Ware ware = mWaren.get(i);
         String name = ware.getName();
         Date verkaufsDatum = ware.getVerkaufsDatum();
         if(verkaufsDatum != null && pName.equals(name) && !verkaufsDatum.before(pDatum)){
            zaehleVerkaufteWare++;
         }
      }

      System.out.println("Automat::gibVerkaufsStatistik("+pName+","+formatter.format(pDatum)+"): " + zaehleVerkaufteWare);
      return zaehleVerkaufteWare;
   }
   
   
   /**
    * Konfiguration einer automatischen Bestellung. <br>
    * Der Automat setzt automatisch Bestellungen ab mittels
    * <code> SystemSoftware.bestellen() </code> wenn eine Ware ausgeht.
    * 
    * @param pWarenName Warenname derjenigen Ware, für welche eine automatische
    *          Bestellung konfiguriert wird.
    * @param pGrenze Grenze bei welcher Anzahl von verbleibenden, nicht
    *          abgelaufener Waren im Automat eine Bestellung abgesetzt werden
    *          soll (Bestellung wenn Waren-Anzahl nicht abgelaufener Waren <=
    *          pGrenze).
    * @param pBestellAnzahl Wieviele neue Waren jeweils bestellt werden sollen.
    */
   public void konfiguriereBestellung(String pWarenName, int pGrenze, int pBestellAnzahl) {
      Bestellung bestell = new Bestellung(pWarenName, pGrenze, pBestellAnzahl);
      
      int index = sucheNameInBestellung(pWarenName);
      if(index < 0){
         // keine Bestellung vorhanden --> neue hinzufügen
         mBestellKonfig.add(bestell);
      } else {
         // Bestellung bereits vorhanden --> ersetzten
         mBestellKonfig.set(index, bestell);
      }
    }
   
   
   /*
    * Überprüft, ob eine Bestellung vorhanden ist und ob 
    * die verkaufte Ware nachbestellt werden muss.
    */
   private void bestelleWare(String pName) {
      int index = sucheNameInBestellung(pName);
      
      // Bestellung vorhanden?
      if(index >= 0){
         // Bestellung vorhanden
         Bestellung bestellKonfig = mBestellKonfig.get(index);
         
         // zähle vorhandene Ware
         int cntWare = 0;
         for(int i = 0; i < mWaren.size(); i++){
            Ware ware = mWaren.get(i);
            if(ware.getName().equals(pName) && (!ware.getIstAbgelaufen()) && (ware.getVerkaufsDatum() == null) ){
               cntWare++;
            }
         }
         
         // genügend Ware vorhanden?
         if(cntWare <= bestellKonfig.getGrenze()){
            // nein --> bestellen
            SystemSoftware.bestellen(bestellKonfig.getBestellName(), bestellKonfig.getBestellmenge());
         }
      }
    }
   
   
   /*
    * sucht nach dem Namen in den Bestellungen.
    * 
    * @return -1 = Fehler/nichts gefunden, sonst index von mBestellKonfig (Beginnend bei 0)
    */
   private int sucheNameInBestellung(String pName) {
      int index = -1;
      // suche, ob eine Bestellung von vorhanden ist.
      for(int i = 0; i < mBestellKonfig.size(); i++){
         Bestellung tmp = mBestellKonfig.get(i);
         if(tmp.getBestellName().equals(pName)){
            index = i;
         }
      }
      
      return index;
    }
   

}